new function () {
    const Build = android.os.Build;
    const Looper = android.os.Looper;
    const MotionEvent = android.view.MotionEvent;
    const CompoundButton = android.widget.CompoundButton;

    const BaseEvent = com.stardust.autojs.core.ui.BaseEvent;
    const ViewExtras = com.stardust.autojs.core.ui.ViewExtras;
    const NativeView = com.stardust.autojs.core.ui.nativeview.NativeView;
    const JsListView = com.stardust.autojs.core.ui.widget.JsListView;

    const Collections = java.util.Collections;
    const ConcurrentHashMap = java.util.concurrent.ConcurrentHashMap;

    const mScope = global;
    const mView = Symbol('mView');
    const mWidget = Symbol('mWidget');
    const mEventEmitter = Symbol('mEventEmitter');
    const mViewAttributes = Symbol('mViewAttributes');
    const mRegisteredEvents = Symbol('mRegisteredEvents');


    function ViewPrototype(view) {
        this[mEventEmitter] = $events.emitter();
        this[mView] = view;
        this[mViewAttributes] = ViewExtras.get(view).getViewAttributes();
        this[mRegisteredEvents] = Collections.newSetFromMap(new ConcurrentHashMap());
    }

    ViewPrototype.prototype = {
        getViewAttributes() {
            return this[mViewAttributes];
        },

        attr(name, value) {
            let attribute = this[mViewAttributes].get(name);
            if (attribute != null) {
                if (arguments.length === 1) {
                    return attribute.get();
                } else {
                    attribute.set(org.mozilla.javascript.ScriptRuntime.toString(value));
                }
            }
            return undefined;
        },

        setWidget(widget) {
            this[mWidget] = widget;
        },

        getWidget() {
            return this[mWidget];
        },

        click(listener) {
            this[mView].performClick();
        },

        longClick(listener) {
            this[mView].performLongClick();
        },

        once(eventName, listener) {
            this.registerEventIfNeeded(eventName);
            return this[mEventEmitter].once(eventName, listener);
        },

        on(eventName, listener) {
            this.registerEventIfNeeded(eventName);
            return this[mEventEmitter].on(eventName, listener);
        },

        addListener(eventName, listener) {
            this.registerEventIfNeeded(eventName);
            return this[mEventEmitter].addListener(eventName, listener);
        },

        registerEventIfNeeded(eventName) {
            if (this[mRegisteredEvents].contains(eventName)) {
                return;
            }
            if (Looper.getMainLooper() == Looper.myLooper()) {
                if (this.registerEvent(eventName)) {
                    this[mRegisteredEvents].add(eventName);
                }
            } else {
                this[mView].post(() => {
                    if (this[mRegisteredEvents].contains(eventName)) {
                        return;
                    }
                    if (this.registerEvent(eventName)) {
                        this[mRegisteredEvents].add(eventName);
                    }
                });
            }

        },

        registerEvent(eventName) {
            switch (eventName) {
                case "touch_down":
                case "touch_up":
                case "touch": {
                    this[mView].setOnTouchListener((v, event) => {
                        let e = new BaseEvent(mScope, event, event.getClass());
                        if (event.getAction() == MotionEvent.ACTION_DOWN) {
                            this.emit("touch_down", e, v);
                        } else if (event.getAction() == MotionEvent.ACTION_UP) {
                            this.emit("touch_up", e, v);
                        } else if (event.getAction() == MotionEvent.ACTION_MOVE) {
                            this.emit("touch_move", e, v);
                        }
                        this.emit("touch", e, v);
                        return e.isConsumed();
                    });
                }
                    return true;
                case "click": {
                    this[mView].setOnClickListener(v => this.emit("click", v));
                }
                    return true;
                case "long_click": {
                    this[mView].setOnLongClickListener(v => {
                        let e = new BaseEvent(mScope, new NativeView.LongClickEvent(v));
                        this.emit("long_click", e, v);
                        return e.isConsumed();
                    });
                }
                    return true;
                case "key": {
                    this[mView].setOnKeyListener((v, keyCode, event) => {
                        let e = new BaseEvent(mScope, event, event.getClass());
                        if (event.getAction() == MotionEvent.ACTION_DOWN) {
                            this.emit("key_down", keyCode, e, v);
                        } else if (event.getAction() == MotionEvent.ACTION_UP) {
                            this.emit("key_up", keyCode, e, v);
                        }
                        this.emit("key", keyCode, e, v);
                        return e.isConsumed();
                    });
                }
                    return true;
                case "scroll_change": {
                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                        this[mView].setOnScrollChangeListener((v, scrollX, scrollY, oldScrollX, oldScrollY) => {
                            let e = new BaseEvent(mScope, new NativeView.ScrollEvent(scrollX, scrollY, oldScrollX, oldScrollY));
                            this.emit("scroll_change", e, v);
                        });
                        return true;
                    }
                }
                    break;
                case "check": {
                    if (this[mView] instanceof CompoundButton) {
                        this[mView].setOnCheckedChangeListener((buttonView, isChecked) =>
                            this.emit("check", isChecked, buttonView));
                    }
                }
                case "item_click":
                case "item_long_click": {
                    if (this[mView] instanceof JsListView) {
                        this[mView].setOnItemTouchListener({
                            onItemClick(listView, itemView, item, pos) {
                                this.emit("item_click", item, pos, itemView, listView);
                            },
                            onItemLongClick(listView, itemView, item, pos) {
                                let e = new BaseEvent(mScope, new NativeView.LongClickEvent(itemView));
                                this.emit("item_long_click", e, item, pos, itemView, listView);
                                return e.isConsumed();
                            }
                        });
                        return true;
                    }
                }
                    break;
            }
            return false;
        },

        emit() {
            return this[mEventEmitter].emit.apply(this[mEventEmitter], arguments);
        },

        eventNames() {
            return this[mEventEmitter].eventNames();
        },

        listenerCount(eventName) {
            return this[mEventEmitter].listenerCount(eventName);
        },

        listeners(eventName) {
            return this[mEventEmitter].listeners(eventName);
        },

        prependListener(eventName, listener) {
            return this[mEventEmitter].prependListener(eventName, listener);
        },

        prependOnceListener(eventName, listener) {
            return this[mEventEmitter].prependOnceListener(eventName, listener);
        },

        removeAllListeners() {
            return this[mEventEmitter].removeAllListeners();
        },

        removeAllListeners(eventName) {
            return this[mEventEmitter].removeAllListeners(eventName);
        },

        removeListener(eventName, listener) {
            return this[mEventEmitter].removeListener(eventName, listener);
        },

        setMaxListeners(n) {
            return this[mEventEmitter].setMaxListeners(n);
        },

        getMaxListeners() {
            return this[mEventEmitter].getMaxListeners();
        },

        defaultMaxListeners() {
            return this[mEventEmitter].defaultMaxListeners();
        }
    }

    module.exports = ViewPrototype;
}